/**
 * Created on 2019-12-4
 *
 * @author ouyf
 * 数据库操作的虚基类，addTrigger，方法需要由子类实现。
 */
package chan.module.project.database.vastdata.bcg;

import java.sql.*;
import java.util.ArrayList;
import java.util.Random;

public abstract class NBCGOperate {

    public String _usrname = "";
    // attributes
    protected Connection con = null;
    // protected static Category log =
    // Category.getInstance(DBOperate.class.getName());
    protected ArrayList objal = null;// 保存当前schema下的表或存储过程名。
    // protected String schema ="";
    protected String dbname = "";
    protected String _url = "";
    protected String _driver = "";
    protected String _usrpwd = "";


    /**
     * open函数用于打开一个数据库可连接。
     *
     * @param url     数据库url。
     * @param driver  JDBC dirver。
     * @param usrname 用户名。
     * @param usrpwd  用户密码。
     * @return 如果连接成功返回true，否则返回false。
     */
    public boolean open(String url, String driver, String usrname, String usrpwd) throws OperateException {

        this._url = url;
        this._driver = driver;
        this._usrname = usrname;
        this._usrpwd = usrpwd;

        try {// 载入JDBC driver的类
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            throw new OperateException(e);
        }

        try {// 打开连接
            con = DriverManager.getConnection(url, usrname, usrpwd);

            con.setAutoCommit(false);
        } catch (SQLException e) {
            throw new OperateException(e);
        }
        // 打开成功返回true.
        return true;
    }

    public void setUserName(String userName) {
        _usrname = userName;
    }

    /**
     * 关闭连接
     */
    public void close() {
        if (con != null) {
            try {
                con.commit();
                // con.setAutoCommit(true);
            } catch (SQLException e) {
            } finally {
                try {
                    con.close();
                } catch (SQLException e) {
                }
            }
        }
    }

    /**
     * addService函数用于添加一个服务
     *
     * @param servicename 服务名
     * @return 如果添加成功返回true，否则返回false
     */
    public boolean addService(String servicename) throws OperateException {
        return false;
    }

    /**
     * addType函数用于添加一个typename
     *
     * @param typename MsgTypeName
     * @return 如果添加成功返回true，否则返回false
     */
    public boolean addType(String typename) throws OperateException {
        return false;
    }

    /**
     * 判断当前的连接是否可用，如果可用返回真。
     *
     * @return 可用返回true, 否则返回false.
     */
    public boolean isValid() {
        try {
            if ((con == null) || con.isClosed()) {
                return false;
            }
        } catch (SQLException e) {
            // throw new OperateException("DB connection is not available");
            return false;
        }
        return true;
    }

    /**
     * 获取连接
     */
    public Connection getConnection() throws OperateException {
        if (isValid()) {
            return this.con;
        } else {
            throw new OperateException("DB connection is not available.");
        }
    }

    /**
     * 判断表或存储过程是否存在于objal.
     *
     * @param cdbname    数据库名，没有的用null ,目前没有用都用null.
     * @param //schema   数据库schema,没有的用null
     * @param objname    要判断BCG的表或存储过程名。
     * @param refreshing 是否要更新当前objal，如果为true则更新当前objal,再判断是否存在于objal中.
     * @return 存在返回ture, 否则返回false.
     */
    public boolean isObjExist(String cdbname, String objname, boolean refreshing) throws OperateException {
        if (!isValid())// 若当前连接不可用返回false
            throw new OperateException("DB connection is not available");
        if (objal == null || refreshing) {
            objal = new ArrayList();
            DatabaseMetaData gmd = null;
            ResultSet rsp = null;// save table metadata
            ResultSet rst = null;// save proc metadata
            String strobj = null;// save current table name or procedure name
            if (objal != null)
                objal.clear();
            try {
                gmd = con.getMetaData();

                String username = gmd.getUserName();// 得到用户名，以用户名作schema

                // zhq test by 20101210 TIS-1016
                String schemaName = null;
//				if (this instanceof OracleDBOperate || this instanceof DM7DBOperate) {
//					schemaName = username;
//				}

                rst = gmd.getTables(null, schemaName, "%", null);// get Table names in
                // this schema.
                rsp = gmd.getProcedures(null, schemaName, "BCG%");// get Procedure names
                // in this schema.
                while (rst.next()) {
                    strobj = rst.getString(3);
                    objal.add(strobj.toLowerCase());// add current table name to objal.
                }
                while (rsp.next()) {
                    strobj = rsp.getString(3);
                    objal.add((strobj).toLowerCase());// add current procedure name to
                    // objal.
                }

            } catch (SQLException e) {
                throw new OperateException(e);
            } finally {
                try {
                    if (rsp != null)
                        rsp.close();
                    if (rst != null)
                        rst.close();
                } catch (SQLException e) {
                }
            }

        } // end if

        if (objal == null)
            return false;
        return objal.contains(objname.toLowerCase());

        // 存在
    }

    /**
     * 根据表名，动作名生成一个触发器的名字，名字是"tablename_actionname_RandomNum"
     *
     * @param tablename  表名
     * @param actiontype 动作名
     * @return 返回触发器名
     */
    protected String generateTriggerName(String tablename, String actiontype) {
        // 取最后一个. 后的部分加到tirggername中
        Random rm = new Random(System.currentTimeMillis());
        int index = tablename.lastIndexOf(".");
        if (index < 0) {
            index = 0;
        } else {
            index = index + 1;
        }
        tablename = tablename.substring(index);

        //中文正则，如果表名中有中文，修改表名为时间戳，避免触发器名称长度问题
        String regex = "^[a-z0-9A-Z\u4e00-\u9fa5]+$";
        if (tablename.matches(regex)) {
            tablename = "etl_" + rm.nextInt(Integer.MAX_VALUE);
        }

        String trigName = rm.nextInt(Integer.MAX_VALUE) + "_" + actiontype + "_" + tablename;
        if (trigName.length() > 30)
            return trigName.substring(0, 29);
        else
            return trigName;

    }

    /**
     * 判断某一个表上是否已经存在触发器，只有对一个表的同一种触发器 只能存在一个的数据库需要实现该方法
     *
     * @param //tablename 表名。
     * @return 如果存在返回true，否则返回false
     */
    protected boolean ifExitSameTriggerOnTable(String tableName) {
        return true;
    }

    /**
     * 判断数据库里是否已经存在同名的触发器
     *
     * @param triggerName 触发器名。
     * @return 如果存在返回true，否则返回false
     */
    protected boolean ifExitSameTriggerName(String triggerName) {
        return true;
    }

    /**
     * 得到原来触发器的内容
     *
     * @param triggerName 触发器名。
     * @return 原来触发器的内容。
     */
    protected String getTriggerContent(String triggerName) {
        return null;
    }

    /**
     * 在触发器跟踪表里添加一条记录
     *
     * @param tableName     表名。
     * @param actionType    监控的动作类型名，可以是insert,update,delete。
     * @param triggerName   触发器名。
     * @param serviceNameID 唯一的服务名ID
     * @param typeName      类型名
     * @return 如果添加成功返回true，否则返回false
     */
    protected boolean addTriggerRecord(String tableName, String actionType, String triggerName, String serviceNameID,
                                       String typeName) throws OperateException {
        if (!isValid())// 若当前连接不可用返回false
            throw new OperateException("DB connection is not available.");
        Statement stm = null;

        String sql = null;

        // sql语句在BCG_MsgService中插入一条记录.
        sql = "insert into " + "nbcg_trigger(ServiceID,TableName,TriggerName,ActionType) values ('" + serviceNameID
                + "','" + tableName + "','" + triggerName + "' ,'" + actionType + "')";
        try {
            stm = con.createStatement();
            int i = stm.executeUpdate(sql);
        } catch (SQLException e) {
            throw new OperateException(e);
        } finally {
            // 清理Statement
            try {
                if (stm != null)
                    stm.close();
            } catch (SQLException e) {
            }
        }
        return true;
    }

    protected boolean addTriggerRecord(String tableName, String schemaName, String actionType, String triggerName,
                                       String serviceNameID, String typeName) throws OperateException {
        if (!isValid())// 若当前连接不可用返回false
            throw new OperateException("DB connection is not available.");
        Statement stm = null;

        String sql = null;

        // sql语句在BCG_MsgService中插入一条记录.
        sql = "insert into " + schemaName + ".NBCG_TRIGGER(ServiceID,TableName,TriggerName,ActionType) values ('"
                + serviceNameID + "','" + tableName + "','" + triggerName + "','" + actionType + "' )";
        try {
            stm = con.createStatement();
            int i = stm.executeUpdate(sql);
        } catch (SQLException e) {
            throw new OperateException(e);
        } finally {
            // 清理Statement
            try {
                if (stm != null)
                    stm.close();
            } catch (SQLException e) {
            }
        }
        return true;
    }

    /**
     * 在触发器跟踪表里删除一条记录
     *
     * @param serviceNameID 唯一的服务名ID
     * @return 如果添加成功返回true，否则返回false
     */
    public boolean deleteTriggerRecord(String serviceNameID) throws OperateException {
        if (!isValid())// 若当前连接不可用返回false
            throw new OperateException("DB connection is not available");
        Statement stm = null;

        String insertsql = null;

        // 删除记录.
        insertsql = "delete from NBCG_TRIGGER where ServiceID  = '" + serviceNameID + "'";
        try {
            stm = con.createStatement();
            int i = stm.executeUpdate(insertsql);
        } catch (SQLException e) {
            throw new OperateException(e);
        } finally {
            // 清理Statement
            try {
                if (stm != null)
                    stm.close();
            } catch (SQLException e) {
            }
        }
        return true;
    }

    /**
     * 得到数据库中某个serviceId的所有触发器名
     *
     * @param //serviceId: 唯一的一个服务ID @return： ArrayList
     */
    protected ArrayList getAllTriggerNameByService(String serviceNameID) throws OperateException {
        if (!isValid())// 若当前连接不可用返回false
            throw new OperateException("DB connection is not available");
        Statement stm = null;
        ResultSet rs = null;
        String selectsql = null;
        String tableName = null;
        String triggerName = null;
        ArrayList triggerNameList = new ArrayList();
        // sql语句在BCG_Trigger中取出一条记录.
        selectsql = "select TableName,TriggerName from NBCG_TRIGGER where ServiceID  = '" + serviceNameID + "'";
        try {
            stm = con.createStatement();
            rs = stm.executeQuery(selectsql);
            while (rs.next()) {

                tableName = rs.getString(1);
                triggerName = rs.getString(2);

                triggerName = getTriggerNameForDelete(tableName, triggerName);

                triggerNameList.add(triggerName);
            }
        } catch (SQLException e) {
            throw new OperateException(e);
        } finally {

            // 清理Statement
            try {
                if (stm != null)
                    stm.close();
                if (rs != null)
                    rs.close();
            } catch (SQLException e) {
            }
        }
        return triggerNameList;
    }

    /**
     * @param tableName,       触发器监控的表的表名，可能包含schema名
     * @param triggerName,触发器名
     * @return
     */
    protected String getTriggerNameForDelete(String tableName, String triggerName) {
        // 多数数据库触发器创建在缺省Schema下，因此删除时同样可不指定schema名。
        return triggerName;
    }

    /**
     * 删除所有和该serviceNameID相关的触发器,该方法只适用于 drop trigger triggername格式就能删除的数据库，不能
     * 如此删除的数据库需要在子类中覆盖该方法。
     *
     * @param serviceNameID
     * @return 删除成功返回true，否则返回false
     */
    public boolean deleteTrigger(String serviceNameID) throws OperateException {
        if (!isValid())// 若当前连接不可用返回false
            throw new OperateException("DB connection is not available");
        Statement stm = null;
        String deletesql = null;
        ArrayList allTriggerName = this.getAllTriggerNameByService(serviceNameID);
        int num = allTriggerName.size();
        for (int i = 0; i < num; i++) {

            // 20101210 modify zhq 把schema.triggerName 分割出 triggerName后和系统表比较 TIS-585
            String tempTrigg = (String) allTriggerName.get(i);
            int index = tempTrigg.indexOf(".");
            if (index > 0) {
                tempTrigg = tempTrigg.substring(index + 1);
            }

            // trigger不存在，不做删除操作
            if (!ifExitSameTriggerName(tempTrigg)) {// 使用不带Schema的trigger名和系统表比较
                continue;
            }

            // 删除记录.
            deletesql = "drop trigger " + allTriggerName.get(i);// 使用可能带schema的trigger删除
            // log.info(insertsql);
            try {
                stm = con.createStatement();
                stm.executeUpdate(deletesql);
            } catch (SQLException e) {
                throw new OperateException(e);
            } finally {
                // 清理Statement
                try {
                    if (stm != null)
                        stm.close();
                } catch (SQLException e) {
                }
            }
        }
        return true;
    }

    /**
     * 添加一个表触发器
     *
     * @param tablename     表名。
     * @param actiontype    监控的动作类型名，可以是insert,update,delete。
     * @param servicename   服务名
     * @param typename      类型名
     * @param al            确定唯一建的字段名和类型
     *                      ,al中的顺序应该是（columnname1,columntype1,columnname2,columntype2...).
     * @param serviceNameID 唯一的服务名ID
     * @return 如果添加成功返回true, 否则返回false.
     */
    public abstract TriggerResult addTrigger(String tablename, String actiontype, String servicename, String typename,
                                             ArrayList al, String serviceNameID) throws OperateException;

    /**
     * 添加一个列触发器
     *
     * @param tablename     列所在的表名。
     * @param ccolname      列名 ，顺序是(colname1,colname2,colname3.....)
     * @param actiontype    监控的动作类型名，只能是update
     * @param servicename   服务名
     * @param typename      类型名
     * @param al            确定唯一建的字段名和类型,al中的顺序应该是（columnname1,columntype1,columnname2,columntype2...).
     * @param serviceNameID 唯一的服务名ID
     * @return 如果添加成功返回true, 否则返回false.
     */
    public abstract TriggerResult addTrigger(String tablename, ArrayList ccolname, String actiontype, String servicename,
                                             String typename, ArrayList al, String serviceNameID) throws OperateException;

    /**
     * 添加一个双向表触发器
     *
     * @param tablename     表名。
     * @param actiontype    监控的动作类型名，可以是insert,update,delete。
     * @param servicename   服务名
     * @param typename      类型名
     * @param al            确定唯一建的字段名和类型
     *                      ,al中的顺序应该是（columnname1,columntype1,columnname2,columntype2...).
     * @param serviceNameID 唯一的服务名ID
     * @return 如果添加成功返回true, 否则返回false. 只实现oracle //added by linan for TIS-721 on
     * 2010.03.12
     */
    public TriggerResult addCycleTrigger(String tablename, String actiontype, String servicename, String typename,
                                         ArrayList al, String serviceNameID) throws OperateException {
        return null;
    }

    /**
     * 添加一个双向列触发器
     *
     * @param tablename     列所在的表名。
     * @param ccolname      列名 ，顺序是(colname1,colname2,colname3.....)
     * @param actiontype    监控的动作类型名，只能是update
     * @param servicename   服务名
     * @param typename      类型名
     * @param al            确定唯一建的字段名和类型,al中的顺序应该是（columnname1,columntype1,columnname2,columntype2...).
     * @param serviceNameID 唯一的服务名ID
     * @return 如果添加成功返回true, 否则返回false. 只实现oracle //added by linan for TIS-721 on
     * 2010.03.12
     */
    public TriggerResult addCycleTrigger(String tablename, ArrayList ccolname, String actiontype, String servicename,
                                         String typename, ArrayList al, String serviceNameID) throws OperateException {
        return null;
    }

}
